package com.example.webfluxdemo.configure;

import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.StringUtils;

import java.time.Duration;

/**
 * Created by victor on 2021/9/28 10:08.
 */
@Configuration
public class RedisConfigure {
    @Bean
    @ConfigurationProperties(prefix = "redis")
    protected MyRedisProperties myRedisProperties() {
        return new MyRedisProperties();
    }

    @Bean
    public ReactiveRedisTemplate<String, Object> redisTemplate(MyRedisProperties myRedisProperties) {
        return createRedisTemplate(createFactory(myRedisProperties));
    }

    private ReactiveRedisTemplate<String, Object> createRedisTemplate(
            ReactiveRedisConnectionFactory connectionFactory) {
        GenericFastJsonRedisSerializer fastJsonRedisSerializer = new GenericFastJsonRedisSerializer();
        ParserConfig.getGlobalInstance().addAccept("com.example.webfluxdemo");
        return new ReactiveRedisTemplate<>(connectionFactory,
                new MySerializationContext(new StringRedisSerializer(), fastJsonRedisSerializer));
    }

    private ReactiveRedisConnectionFactory createFactory(MyRedisProperties properties) {
        /* ========= 基本配置 ========= */
        RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
        configuration.setHostName(properties.getHost());
        configuration.setPort(properties.getPort());
        configuration.setDatabase(properties.getDatabase());
        if (StringUtils.hasText(properties.getPassword())) {
            RedisPassword redisPassword = RedisPassword.of(properties.getPassword());
            configuration.setPassword(redisPassword);
        }

        /* ========= 连接池通用配置 ========= */
        @SuppressWarnings("rawtypes")
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxTotal(properties.getMaxActive());
        genericObjectPoolConfig.setMinIdle(properties.getMinIdle());
        genericObjectPoolConfig.setMaxIdle(properties.getMaxIdle());
        genericObjectPoolConfig.setMaxWaitMillis(properties.getMaxWait());

        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
        builder.poolConfig(genericObjectPoolConfig);
        builder.commandTimeout(Duration.ofMillis(properties.getTimeout()));
        LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(configuration, builder.build());
        connectionFactory.afterPropertiesSet();

        return connectionFactory;
    }

    static class MySerializationContext implements RedisSerializationContext<String, Object> {
        private final StringRedisSerializer stringRedisSerializer;
        private final GenericFastJsonRedisSerializer fastJsonRedisSerializer;

        MySerializationContext(StringRedisSerializer stringRedisSerializer,
                GenericFastJsonRedisSerializer fastJsonRedisSerializer) {
            this.stringRedisSerializer = stringRedisSerializer;
            this.fastJsonRedisSerializer = fastJsonRedisSerializer;
        }

        @Override
        public SerializationPair<String> getKeySerializationPair() {
            return RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer);
        }

        @Override
        public SerializationPair<Object> getValueSerializationPair() {
            return RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer);
        }

        @Override
        @SuppressWarnings("unchecked")
        public SerializationPair<String> getHashKeySerializationPair() {
            return RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer);
        }

        @Override
        public SerializationPair<Object> getHashValueSerializationPair() {
            return RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer);
        }

        @Override
        public SerializationPair<String> getStringSerializationPair() {
            return RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer);
        }
    }
}
